<!DOCTYPE html>
<html>
<!--
Copyright 2008 The Closure Library Authors. All Rights Reserved.

Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<!--

  @author eae@google.com (Emil A Eklund)
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Closure Unit Tests - goog.ui.FilteredMenu</title>
<style>
.goog-menu {
  position: absolute;
  background: #eee;
  border: 1px solid #aaa;
  font: menu;
}
.goog-menu-content {
  height: 2em;
  overflow: auto;
}

#testmenu {
  right: 5px;
  bottom: 5px;
}
</style>
<script src="../base.js"></script>
<script>
  goog.require('goog.a11y.aria');
  goog.require('goog.a11y.aria.AutoCompleteValues');
  goog.require('goog.a11y.aria.State');
  goog.require('goog.dom');
  goog.require('goog.events');
  goog.require('goog.events.Event');
  goog.require('goog.events.EventType');
  goog.require('goog.events.KeyCodes');
  goog.require('goog.math.Rect');
  goog.require('goog.style');
  goog.require('goog.testing.events');
  goog.require('goog.testing.jsunit');
  goog.require('goog.ui.FilteredMenu');
  goog.require('goog.ui.MenuItem');
</script>
</head>
<body>
<div>
  <div id="wrapper">
    <div id="sandbox"></div>
  </div>

  <div id="testmenu" class="goog-menu goog-menu-vertical">
    <div class="goog-menu-filter">
      <div></div>
      <input tabindex="0" autocomplete="off" type="text">
    </div>
    <div class="goog-menu-content">
      <div class="goog-menuitem">Apple<span class="goog-menuitem-accel">A</span></div>
      <div class="goog-menuitem">Lemon</div>
      <div class="goog-menuitem">Orange</div>
      <div class="goog-menuitem">Strawberry</div>
    </div>
  </div>

</div>
<script>

var sandbox;

function setUp() {
  sandbox = goog.dom.getElement('sandbox');
}


function tearDown() {
  sandbox.innerHTML = '';
}


function testRender() {
  menu = new goog.ui.FilteredMenu();
  menu.addItem(new goog.ui.MenuItem('Menu Item 1'));
  menu.addItem(new goog.ui.MenuItem('Menu Item 2'));
  menu.render(sandbox);

  assertEquals('Menu should contain two items.', 2, menu.getChildCount());
  assertEquals('Caption of first menu item should match supplied value.',
               'Menu Item 1',
               menu.getItemAt(0).getCaption());
  assertEquals('Caption of second menu item should match supplied value.',
               'Menu Item 2',
               menu.getItemAt(1).getCaption());
  assertTrue('Caption of first item should be in document.',
              sandbox.innerHTML.indexOf('Menu Item 1') != -1);
  assertTrue('Caption of second item should be in document.',
              sandbox.innerHTML.indexOf('Menu Item 2') != -1);

  menu.dispose();
}


function testDecorate() {
  menu = new goog.ui.FilteredMenu();
  menu.decorate(goog.dom.getElement('testmenu'));

  assertEquals('Menu should contain four items.', 4, menu.getChildCount());
  assertEquals('Caption of menu item should match decorated element',
               'Apple',
               menu.getItemAt(0).getCaption());
  assertEquals('Accelerator of menu item should match accelerator element',
               'A',
               menu.getItemAt(0).getAccelerator());
  assertEquals('Caption of menu item should match decorated element',
               'Lemon',
               menu.getItemAt(1).getCaption());
  assertEquals('Caption of menu item should match decorated element',
               'Orange',
               menu.getItemAt(2).getCaption());
  assertEquals('Caption of menu item should match decorated element',
               'Strawberry',
               menu.getItemAt(3).getCaption());

  menu.dispose();
}


function testFilter() {
  menu = new goog.ui.FilteredMenu();
  menu.addItem(new goog.ui.MenuItem('Family'));
  menu.addItem(new goog.ui.MenuItem('Friends'));
  menu.addItem(new goog.ui.MenuItem('Photos'));
  menu.addItem(new goog.ui.MenuItem([
    goog.dom.createTextNode('Work'),
    goog.dom.createDom('span', goog.ui.MenuItem.ACCELERATOR_CLASS, 'W')
  ]));

  menu.render(sandbox);

  // Check menu items.
  assertEquals('Family should be the first label in the move to menu',
               'Family', menu.getChildAt(0).getCaption());
  assertEquals('Friends should be the second label in the move to menu',
               'Friends', menu.getChildAt(1).getCaption());
  assertEquals('Photos should be the third label in the move to menu',
               'Photos', menu.getChildAt(2).getCaption());
  assertEquals('Work should be the fourth label in the move to menu',
               'Work', menu.getChildAt(3).getCaption());

  // Filter menu.
  menu.setFilter('W');
  assertFalse('Family should not be visible when the menu is filtered',
              menu.getChildAt(0).isVisible());
  assertFalse('Friends should not be visible when the menu is filtered',
              menu.getChildAt(1).isVisible());
  assertFalse('Photos should not be visible when the menu is filtered',
              menu.getChildAt(2).isVisible());
  assertTrue('Work should be visible when the menu is filtered',
             menu.getChildAt(3).isVisible());
  // Check accelerator.
  assertEquals('The accelerator for Work should be present',
               'W', menu.getChildAt(3).getAccelerator());

  menu.setFilter('W,');
  for (var i = 0; i < menu.getChildCount(); i++) {
    assertFalse('W, should not match anything with allowMultiple set to false',
               menu.getChildAt(i).isVisible());
  }

  // Clear filter.
  menu.setFilter('');
  for (var i = 0; i < menu.getChildCount(); i++) {
    assertTrue('All items should be visible', menu.getChildAt(i).isVisible());
  }

  menu.dispose();
}


function testFilterAllowMultiple() {
  menu = new goog.ui.FilteredMenu();
  menu.setAllowMultiple(true);
  menu.addItem(new goog.ui.MenuItem('Family'));
  menu.addItem(new goog.ui.MenuItem('Friends'));
  menu.addItem(new goog.ui.MenuItem('Photos'));
  menu.addItem(new goog.ui.MenuItem('Work'));

  menu.render(sandbox);

  // Filter menu.
  menu.setFilter('W,');
  for (var i = 0; i < menu.getChildCount(); i++) {
    assertTrue('W, should show all items with allowMultiple set to true',
               menu.getChildAt(i).isVisible());
  }

  // Filter second label.
  menu.setFilter('Work,P');
  assertFalse('Family should not be visible when the menu is filtered',
              menu.getChildAt(0).isVisible());
  assertFalse('Friends should not be visible when the menu is filtered',
              menu.getChildAt(1).isVisible());
  assertTrue('Photos should be visible when the menu is filtered',
             menu.getChildAt(2).isVisible());
  assertFalse('Work should not be visible when the menu is filtered',
              menu.getChildAt(3).isVisible());

  // Clear filter.
  menu.setFilter('');
  for (var i = 0; i < menu.getChildCount(); i++) {
    assertTrue('All items should be visible', menu.getChildAt(i).isVisible());
  }

  menu.dispose();
}


function testFilterWordBoundary() {
  menu = new goog.ui.FilteredMenu();
  menu.addItem(new goog.ui.MenuItem('Vacation Photos'));
  menu.addItem(new goog.ui.MenuItem('Work'));
  menu.addItem(new goog.ui.MenuItem('Receipts & Invoices'));
  menu.addItem(new goog.ui.MenuItem('Invitations'));
  menu.addItem(new goog.ui.MenuItem('3.Family'));
  menu.addItem(new goog.ui.MenuItem('No:Farm'));
  menu.addItem(new goog.ui.MenuItem('Syd/Family'));

  menu.render(sandbox);

  // Filter menu.
  menu.setFilter('Photos');
  assertTrue('Vacation Photos should be visible when the menu is filtered',
             menu.getChildAt(0).isVisible());
  assertFalse('Work should not be visible when the menu is filtered',
              menu.getChildAt(1).isVisible());
  assertFalse('Receipts & Invoices should not be visible when the menu is ' +
              'filtered',
              menu.getChildAt(2).isVisible());
  assertFalse('Invitations should not be visible when the menu is filtered',
              menu.getChildAt(3).isVisible());

  menu.setFilter('I');
  assertFalse('Vacation Photos should not be visible when the menu is filtered',
             menu.getChildAt(0).isVisible());
  assertFalse('Work should not be visible when the menu is filtered',
              menu.getChildAt(1).isVisible());
  assertTrue('Receipts & Invoices should be visible when the menu is filtered',
             menu.getChildAt(2).isVisible());
  assertTrue('Invitations should be visible when the menu is filtered',
             menu.getChildAt(3).isVisible());

  menu.setFilter('Fa');
  assertTrue('3.Family should be visible when the menu is filtered',
             menu.getChildAt(4).isVisible());
  assertTrue('No:Farm should be visible when the menu is filtered',
             menu.getChildAt(5).isVisible());
  assertTrue('Syd/Family should be visible when the menu is filtered',
             menu.getChildAt(6).isVisible());

  menu.dispose();
}


function testScrollIntoView() {
  menu = new goog.ui.FilteredMenu();
  menu.addItem(new goog.ui.MenuItem('Family'));
  menu.addItem(new goog.ui.MenuItem('Friends'));
  menu.addItem(new goog.ui.MenuItem('Photos'));
  menu.addItem(new goog.ui.MenuItem('Work'));
  menu.render(sandbox);

  menu.setHighlightedIndex(0);
  assertTrue('Highlighted item should be visible', isHighlightedVisible(menu));
  menu.setHighlightedIndex(1);
  assertTrue('Highlighted item should be visible', isHighlightedVisible(menu));
  menu.setHighlightedIndex(2);
  assertTrue('Highlighted item should be visible', isHighlightedVisible(menu));
  menu.setHighlightedIndex(3);
  assertTrue('Highlighted item should be visible', isHighlightedVisible(menu));
  menu.setHighlightedIndex(0);
  assertTrue('Highlighted item should be visible', isHighlightedVisible(menu));

  menu.dispose();
}

function testEscapeKeyHandling() {
  menu = new goog.ui.FilteredMenu();
  menu.addItem(new goog.ui.MenuItem('Family'));
  menu.addItem(new goog.ui.MenuItem('Friends'));
  menu.render(sandbox);

  var gotKeyCode = false;
  var wrapper = document.getElementById('wrapper');
  goog.events.listenOnce(wrapper, goog.events.EventType.KEYPRESS, function(e) {
    gotKeyCode = true;
  });
  goog.testing.events.fireKeySequence(menu.getFilterInputElement(),
      goog.events.KeyCodes.ESC);
  assertFalse('ESC key should not propagate out to parent', gotKeyCode);
}


function testAriaRoles() {
  menu = new goog.ui.FilteredMenu();
  menu.addItem(new goog.ui.MenuItem('Item 1'));
  menu.render(sandbox);

  var input = menu.getFilterInputElement();
  assertEquals(goog.a11y.aria.AutoCompleteValues.LIST,
      goog.a11y.aria.getState(input, goog.a11y.aria.State.AUTOCOMPLETE));
  assertEquals(menu.getContentElement().id,
      goog.a11y.aria.getState(input, goog.a11y.aria.State.OWNS));
  assertEquals('true',
      goog.a11y.aria.getState(input, goog.a11y.aria.State.EXPANDED));
}


function testInputActiveDecendant() {
  menu = new goog.ui.FilteredMenu();
  var menuItem1 = new goog.ui.MenuItem('Item 1');
  var menuItem2 = new goog.ui.MenuItem('Item 2');
  menu.addItem(menuItem1);
  menu.addItem(menuItem2);
  menu.render(sandbox);

  assertNull(goog.a11y.aria.getActiveDescendant(menu.getFilterInputElement()));
  menu.setHighlightedIndex(0);
  assertEquals(menuItem1.getElementStrict(),
      goog.a11y.aria.getActiveDescendant(menu.getFilterInputElement()));
  menu.setHighlightedIndex(1);
  assertEquals(menuItem2.getElementStrict(),
      goog.a11y.aria.getActiveDescendant(menu.getFilterInputElement()));
}


function isHighlightedVisible(menu) {
  var contRect = goog.style.getBounds(menu.getContentElement());
  // Expands the containing rectangle by 1px on top and bottom. The test
  // sometime fails with 1px out of bound on FF6/Linux. This is not
  // consistently reproducible.
  contRect = new goog.math.Rect(
      contRect.left, contRect.top - 1, contRect.width, contRect.height + 2);
  var itemRect = goog.style.getBounds(menu.getHighlighted().getElement());
  return contRect.contains(itemRect);
}


</script>
</body>
</html>
